package com.qz.queue;

import java.util.Scanner;

/**
 * @Description
 * @Author Flag
 * @Date: 2021/7/4 9:31
 * @Version: 1.0
 **/
public class CircleArrayQueueDome {
    public static void main(String[] args) {
        //创建一个队列
        CircleArray circleQueue = new CircleArray(4);
        //接收用户输入
        char key = ' ';
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;
        //输出一个菜单
        while (loop){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列取出数据");
            System.out.println("h(head):查看队列头的数据");
            key = scanner.next().charAt(0);
            switch (key){
                case 's':
                    circleQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("请输入一个数:");
                    int value = scanner.nextInt();
                    circleQueue.addQueue(value);
                    break;
                case 'g': //取出数据
                    try {
                        int res = circleQueue.getQueue();
                        System.out.printf("取出数据是%d\n",res);
                    } catch (Exception e) {
                        System.out.printf(e.getMessage());
                    }
                    break;
                case 'h': //查看队列头的数据
                    try {
                        int res = circleQueue.headQueue();
                        System.out.printf("队列头的数据是%d\n",res);
                    } catch (Exception e) {
                        System.out.printf(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    System.out.println("程序退出");
                    break;
                default:
                    break;
            }

        }
    }
}


/**
 * 使用数据模拟队列-编写一个CircleArray类
 */
class CircleArray{
    private int maxSize;//表示数组的最大容量
    //front变量的含义做一个调整：front就指向队列的第一个元素，也就是说arr[front]就是队列的第一个元素,
    //front的初始值是0
    private int front;
    //rear变量的含义做一个调整：rear指向队列的最后一个元素的后一个位置，因为希望空出一个空间作为约定
    //rear的初始值是0
    private int rear;
    private int[] arr;//该数据用于用于存储数据，模拟队列

    /**
     * 创建队列的构造器
     * @param maxSize
     */
    public CircleArray(int maxSize) {
        this.maxSize = maxSize;
        this.arr = new int[this.maxSize];
    }

    /**
     * 判断队列是否已满
     * @return
     */
    public boolean isFull(){
        //因为约定好了。rear是指向尾元素的后一个元素，
        // 并且区分空和满，1.如果数组为null，则rear==front；2.始终留一个位置来区分满了的情况，否则不能区分是满还是空
        //rear+1代表rear后一个的后一个，取余是因为如果raer是最后一个,在+1会越界,所以对于maxSize取余,如果此时的值==front,则证明rear和front正向差1,则代表队列已经满了
        return (rear+1)%maxSize == front;
    }

    /**
     * 判断队列是否为null
     * @return
     */
    public boolean isEmpty(){
        //如果rear==front,代表rear和front重合，代表队列已满
        return rear == front;
    }

    /**
     * 添加数据到队列
     * @param n
     */
    public void addQueue(int n){
        //判断队列是否已满
        if(isFull()){
            System.out.println("队列满，不能加入数据");
            return;
        }
        //因为此时的rear代表尾巴的后一个元素,所以直接将新元素添加到rear位置上
        arr[rear] = n;
        //如果rear是最后一个元素(此处不考虑数组已满,因为满了的情况已经排除),
        // 在+1就溢出了,所以需要取模：
        // 1.如果是最后一个+1取模就是第一个;
        // 2.如果不是最后一个,+1取模还是原来位置
        rear = (rear+1)%maxSize;
    }


    /**
     * 从队列中获取数据
     * @return
     */
    public int getQueue(){
        //判断队列是否空
        if(isEmpty()){
            throw new RuntimeException("队列中没有数据，不能取出");
        }

        //这里需要分析出front是指向队列的第一个元素
        //1.先把front对应的值保留到一个临时变量中,因为不放到临时变量的话，移动以后就拿不到了
        int value = arr[front];
        //2.将front后移，考虑取模，防止溢出
        front = (front+1)%maxSize;
        //3.将临时保存的变量返回
        return value;
    }

    /**
     * 显示队列的所有数据
     */
    public void showQueue(){
        if(isEmpty()){
            System.out.println("队列为null，不能打印");
            return;
        }
        //思路:从front开始遍历，遍历多少个元素
        //遍历多少个元素,是求数组中实际的元素的数量
        //从front到front+size(),就是从front开始，遍历到size()个元素---->size()返回数组的实际元素个数
        for (int i = front; i < front + size(); i++) {
            //这里需要考虑到,如果front不是数组的首元素,则一定会存在front+size()，大于maxSize的情况,则会出现下标越界,所以需要取模
            System.out.printf("arr[%d]=%d\n", i%maxSize ,arr[i%maxSize]);
        }
    }

    /**
     * 显示队列的头数据，注意不是取出数据
     * @return
     */
    public int headQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列为null,没有头部数据");
        }
        //font指向队列头的前一个数据，所以需要加一
        return arr[front];
    }


    /**
     * 求出当前队列有效元素的个数
     * @return
     */
    public int size(){
        //rear+maxsize是指:让rear足够大，防止rear如果小于front,得到的结果是负值
        //让rear+maxSize-front是front到rear的元素个数(粗略计算),
        //->1.如果rear>front,则rear+maxSize-front是相当于元素的实际数量+maxSize
        //->2.如果rear<front,则rear+maxSize-front是元素的实际数量
        //->3.如果rear==front,则rear+maxSize-front是maxSize

        //不管什么情况对于maxsize取余，都可以得到数组元素的个数
        return (rear+ maxSize - front ) % maxSize;
    }

}

